package charts;
import charts.series.Element;
import charts.Base;
import flash.geom.Point;
import elements.axis.XAxisLabels;

class ObjectCollection
{
	public var sets:Array<Base>;
	public var groups: Float;
	
	public function new () {
		sets = new Array<Base>();
	}
	
	public function add ( set:Base ): Void {
		sets.push( set );
	}
	
	
	public function get_max_x(): Float {
		
		var max: Float = Math.NEGATIVE_INFINITY;

		for ( o in sets )
			max = Math.max( max, o.get_max_x() );

		return max;
	}
	
	public function get_min_x(): Float {
		
		var min: Float = Math.POSITIVE_INFINITY;

		for ( o in sets )
			min = Math.min( min, o.get_min_x() );

		return min;
	}
	
	// get x, y co-ords of vals
	public function resize ( sc:ScreenCoordsBase ):Void {
		for ( o in sets )
			o.resize( sc );
	}
	
	/**
	* Tell each set to update the tooltip string and
	* eplace all #x_label# with the label
	* 
	* @param	labels
	*/
	public function tooltip_replace_labels( labels:XAxisLabels ):Void {
		
		for ( o in sets )
			o.tooltip_replace_labels( labels );			
	}
	
	public function mouse_out():Void {
		for ( s in sets )
			s.mouse_out();
	}
	
	
	function closest ( x: Float, y: Float ):Element {
		var o:Dynamic;
		var s:Base;
		
		// get closest points from each data set
		var closest = new Array<Element>();
		for( s in sets )
			closest.push( s.closest( x, y ) );
		
		// find closest point along X axis
		var min: Float = Math.POSITIVE_INFINITY;
		for( o in closest )
			min = Math.min( min, o.distance_x );
			
		//
		// now select all points that are the
		// min (see above) distance along the X axis
		//
		var xx : {element:Element , distance_x:Float , distance_y:Float} = 
			{element:null, distance_x: Math.POSITIVE_INFINITY, distance_y: Math.POSITIVE_INFINITY };
		// TODO - check if POSITIVE_INFINITY is a good replacement for Math.POSITIVE_INFINITY
		for( o in closest ) {
			if( o.distance_x == min )
			{
				// these share the same X position, so choose
				// the closest to the mouse in the Y
				if( o.distance_y < xx.distance_y )
					xx = o;
			}
		}
		
		// pie charts may not return an element
		if( xx.element )
			xx.element.set_tip( true );
			
		return xx.element;
	}
	
	/*
	
	hollow
		line --> ------O---------------O-----
			
				+-----+
				|  B  |
			+-----+     |   +-----+
			|  A  |     |   |  C  +- - -
			|     |     |   |     |  D
			+-----+-----+---+-----+- - -
				1    2
		
	*/
	public function mouse_move( x: Float, y: Float ):Element {
		//
		// is the mouse over, above or below a
		// bar or point? For grouped bar charts,
		// two bars will share an X co-ordinate
		// and be the same distance from the
		// mouse. For example, if the mouse is
		// in position 1 in diagram above. This
		// filters out all items that are not
		// above or below the mouse:
		//
		var e:Element;// this.inside__(x, y);
		
		//if ( e == null )
		//{
			//
			// no Elements are above or below the mouse,
			// so we select the BEST item to show (mouse
			// is in position 2)
			//
			e = this.closest(x, y);
		//}
		
		return e;
	}
	
	
	//
	// Usually this will return an Array of one Element to
	// the Tooltip, but some times 2 (or more) Elements will
	// be on top of each other
	//
	public function closest_2( x: Float, y: Float ): Array<Element> {

		var e:Element;
		var s:Base;
		var p:flash.geom.Point;
		
		//
		// get closest points from each data set
		//
		var closest = new Array<Element>();
		for( s in sets ) {
			var arr:Array<Element> = s.closest_2( x, y );
			for( e in arr )
				closest.push( e );
		}
		
		//
		// find closest point along X axis
		// different sets may return Elements
		// in different X locations
		//
		var min_x: Float = Math.POSITIVE_INFINITY;
		for( e in closest ) {
			
			p = e.get_mid_point();
			min_x = Math.min( min_x, Math.abs( x - p.x ) );
		}
		
		//
		// filter out the Elements that
		// are too far away along the X axis
		//
		var good_x = new Array<Element>();
		for( e in closest ) {
			
			p = e.get_mid_point();
			if ( Math.abs( x - p.x ) == min_x )
				good_x.push( e );
		}
		
		//
		// now get min_y from filtered array
		//
		var min_y: Float = Math.POSITIVE_INFINITY;
		for( e in good_x ) {
			
			p = e.get_mid_point();
			min_y = Math.min( min_y, Math.abs( y - p.y ) );
		}
		
		//
		// now filter out any that are not min_y
		//
		var good_x_and_y = new Array<Element>();
		for( e in good_x ) {
			
			p = e.get_mid_point();
			if ( Math.abs( y - p.y ) == min_y )
				good_x_and_y.push( e );
		}

		return good_x_and_y;
	}
	
	//
	// find the closest point to the mouse
	//
	public function mouse_move_proximity( x: Float, y: Float ): Array<Element> {
		var e:Element;
		var s:Base;
		var p:flash.geom.Point;
		
		//
		// get closest points from each data set
		//
		var closest = new Array<Element>();
		for( s in sets ) {
			
			var arr:Array<Element> = s.mouse_proximity( x, y );
			for( e in arr )
				closest.push( e );
		}
		
		//
		// find the min distance to these
		//
		var min_dist: Float = Math.POSITIVE_INFINITY;
		var mouse:flash.geom.Point = new flash.geom.Point(x, y);
		for( e in closest ) {
			min_dist = Math.min( flash.geom.Point.distance(e.get_mid_point(), mouse), min_dist );
		}
		
		// keep these closest Elements
		var close:Array<Element> = [];
		for( e in closest ) {
			if ( flash.geom.Point.distance(e.get_mid_point(), mouse) == min_dist )
				close.push( e );
		}
		
		return close;
	}
	
	//
	// are we resizing a PIE chart?
	//
	public function has_pie(): Bool {
		if (sets.length > 0 && Std.is( sets[0],Pie ))
			return true;
		return false;
	}
	
	/**
		* To stop memory leaks we explicitly kill all
		* our children
		*/
	public function die (): Void {
		for ( o in sets )
			o.die();
	}
}
